;==============================================================================
;Assumptions
;==============================================================================
;The 2 best mobile plans were derived based on the estimated calculated price 
;of the mobile plans. The different usage patterns of each mobile plan is not
;taken into account.

;==============================================================================
;Declare global variable
;==============================================================================
(define choice 0)
(define outgoingMinutes 0)
(define numberOfSMS 0)
(define dataUsage 0)
(define calculatedPlans '())
(define calculatedPrice '())
(define additionalOutgoingMinutesCost 0) ;store additionalOutgoingMinutesCost
(define additionalSMSCost 0)             ;store additionalSMSCost
(define additionalDataUsageCost 0)       ;store additionalDataUsageCost
(define minPrice 0)               ;define the minimum price among all plans
(define best_plan '())                   ;store the best plans 
(define withoutFirstBest '())            ;list to store plans without the first best plans
(define count 0)                         ;variable for finding length of list

;==============================================================================
;All available plans 
;==============================================================================
(define mobile_phone_plans
   (list 
    '("Singtel" "iOne Super Value   " 80        50         0       15.00)
    '("Singtel" "iOne Plus          " 100       500        0       25.68)
    '("Singtel" "iTwo Value         " 300       500        0       48.15)
    '("Singtel" "iTwo Plus          " 700       500        0       82.93)
    '("Singtel" "iThree Plus        " 2000      2000       0       192.60)
    '("Singtel" "3G Flexi Lite      " 100       800        2       39.90)
    '("Singtel" "3G Flexi Value     " 200       900        3       59.90)
    '("Singtel" "3G Flexi Plus      " 500       1000       4       99.90)
    '("Singtel" "3G Flexi Premium   " 2000      2500       12      205.00)
    '("Starhub" "SmartSurf Lite     " 100       800        2       38.00)
    '("Starhub" "SmartSurf Value    " 300       900        4       58.00)
    '("Starhub" "SmartSurf Premium  " 700       1000       6       98.00)
    '("Starhub" "SmartSurf Elite    " 2000      2500       12      205.00)
    '("Starhub" "Powervalue 80      " 80        0          0       20.33)
    '("Starhub" "Powervalue 100     " 100       500        0       25.68)
    '("Starhub" "Powervalue 300     " 300       500        0       48.15)
    '("Starhub" "Powervalue 700     " 700       500        0       82.93)
    '("M1     " "ValueSurf Plus     " 120       600        2       39.00)
    '("M1     " "LiteSurf  Plus     " 300       800        3       59.00)
    '("M1     " "ExtremeSurf Plus   " 700       1000       5       98.00)
    '("M1     " "SunSaver           " 100       500        0       25.68)
    '("M1     " "SunSaver Plus      " 300       500        0       48.15)
    '("M1     " "SunMax             " 700       500        0       82.93)
    '("M1     " "SunMax Plus        " 2000      2000       0       195.81))
)

;====================================================================================
;Display Menu
;====================================================================================
(define(mainMenu)
  (newline)
  (display " ============ Main Menu ============")(newline)
  (display " 1) Display all plans") (newline)
  (display " 2) Recommend Two best plans from your monthly usage pattern")(newline)
  (display " 3) Exit")(newline)
)

;Main menu selections - user enter 1 for displaying all plans, 2 for recommendation of two best plans 
;from their inputs
(define (selection)
   (case choice
     ((1)(newline)
        (display "Displaying all existing mobile plans: ")(newline)
        (display "Provider        Mobile Plan           \tOutgoing Minutes \tFree SMS\tData Bundle \tPrice")(newline)
        (displayAllPlans mobile_phone_plans)           ;find all existing mobile plans and display them
       
        (backToMainMenu)                               ;Go back main menu
      ) 
     ((2)(newline)
        (set! best_plan '())
        (getUserMonthlyUsage)                          ;prompt user for inputs
        (calculateUsagePrice mobile_phone_plans)       ;based on the inputs,calculate expected price for each plan
        (comparePlans calculatedPlans)                 ;compare all the plans among themselves and find the minimum price
        (search-for-plans calculatedPlans)             ;search for the plans with the minPrice in the calculatedPlans list
        
        (display "The BEST plans are as follows: ")(newline)
        (display "Provider        Mobile Plan           \tOutgoing Minutes \tFree SMS\tData Bundle \tExpected Price")
        (newline)
        (findList best_plan)                           ;find the length of the best plan list
    
        ;if there are exactly 2 best plans, just print them
        (displayAllPlans best_plan)
        
        ;if the length of the best_plan for first computation is < 2 then we recompute to get 2nd best plan
        (if(< count 2)
           (begin
              ;computing the 2nd best_plan
              (remove-first-best calculatedPlans)
              (set! minPrice 0)                      ;reset minPrice to the default
              (set! best_plan '())                          ;clear the best_plan list for recomputation
              (comparePlans withoutFirstBest)
              (search-for-plans withoutFirstBest)
              (displayAllPlans best_plan) ;need to check if this return more than 2
           )
           
        )
        
        ;reset all variables and lists for recomputation.
        (reset)
        
        (backToMainMenu)                               ;Go back main menu
      )
     ((3)(newline)
        (display "Thank you for using this program. See you next time! ")
      )
      (else ;if the input value is not within the option range, return to menu.
       (begin
         (newline)
         (display "Please enter a number within the option range. ")
         (backToMainMenu)
       ))
   ) 
 ) 


;==============================================================================================
;Functions
;==============================================================================================
;allow users to go back to menu selection page
(define (backToMainMenu)
  (newline)
  (mainMenu)                               ;call mainMenu function for display options
  (display "Enter your choice")(newline)   
  (set! choice (read))                     ;set the variable choice according to user input
  (selection)                              ;direct user to specific option
)

;validate user input
(define (validateOutgoing input)
  (if(and (integer? input) (>= input 0))
     (set! outgoingMinutes input)
     (begin
          (newline)
          (display "Please enter an integer value 0 or larger.")(newline)
          (display "Please enter the estimated amount of monthly outgoing minutes:")(newline)
          (validateOutgoing (read))
     )
  )
)

(define (validateNumberOfSMS input)
  (if(and (integer? input) (>= input 0))
     (set! numberOfSMS input)
     (begin
       (newline)
       (display "Please enter an integer value 0 or larger.")(newline)
       (display "Please enter the estimated number of SMSes sent monthly:")(newline)
       (validateNumberOfSMS (read))
     )
  )
)

(define (validateDataUsage input)
  (if(and (integer? input) (>= input 0))
     (set! dataUsage input)
     (begin
       (newline)
       (display "Please enter an integer value 0 or larger.")(newline)
       (display "Please enter the estimated amount of monthly data usage:")(newline)
       (validateDataUsage (read))
     )
  )
)
;prompt user for inputs
(define (getUserMonthlyUsage)
   (display "Please enter the estimated amount of monthly outgoing minutes:")(newline)
   (validateOutgoing (read)) 
   (newline)
   
   (display "Please enter the estimated number of SMSes sent monthly:")(newline)
   (validateNumberOfSMS (read))
   (newline)
  
   (display "Please enter the estimated amount of monthly data usage:")(newline)
   (validateDataUsage (read))
   (newline)
)

;Print a specific mobile plan
(define (PrintMobilePlan mobileplan)
    (cond((null? mobileplan) '())
         (else (begin (display (car mobileplan)) (display "\t       ")
                      (PrintMobilePlan (cdr mobileplan)) ;Recurse to print next piece of information
                ) 
         ) 
    ) 
)
 
 ;Traverse through all plans in argument list and print them
 (define (displayAllPlans list)
    (cond((null? list) (display "No mobile plan existing."))
         (else
           (PrintMobilePlan (car list))  ;Print plan information
           (newline) 
           (if(null? (cdr list)) ;Check if is empty list
           (display "") ;If is empty list, do not do anything
           (displayAllPlans (cdr list));Else recurse to print next plan information
           ) 
        ) 
    ) 
  )
;calculate expected price of each plans based on user input
 (define (calculateUsagePrice mobileList)
   (cond
        ((null? mobileList) 
          (display "No mobile plan existing."))
        (else
          (computeEachPlan (car mobileList))
          (if(null? (cdr mobileList))
             (display "")
             (calculateUsagePrice (cdr mobileList))
          )
        )
    )
 ) 
 
 ;computation function for the expected price, called by calculateUsagePrice function
  (define (computeEachPlan eachMobilePlan)
     (if (null? eachMobilePlan) '()
          ;begin of else statements
          (begin 
              (if (<= outgoingMinutes (caddr eachMobilePlan))
                   (set! additionalOutgoingMinutesCost 0)
                   ;if user input outgoingMinutes > mobile plan's outgoing minutes
                   (set! additionalOutgoingMinutesCost (* 0.16 (- outgoingMinutes (caddr eachMobilePlan))))
              )
              
              (if (<= numberOfSMS (cadddr eachMobilePlan))
                   (set! additionalSMSCost 0)
                   ;if user input numberOfSMS > mobile plan's SMS limit
                   (set! additionalSMSCost (* 0.05 (- numberOfSMS (cadddr eachMobilePlan))))
              )
              
              (if (<= dataUsage (cadddr(cdr eachMobilePlan)))
                   (set! additionalDataUsageCost 0)
                   ;if user input numberOfSMS > mobile plan's SMS limit
                   (begin
                     (if (or (string=? "Singtel" (car eachMobilePlan)) (string=? "M1" (car eachMobilePlan)))
                         (set! additionalDataUsageCost (* 5.35 (- dataUsage (cadddr(cdr eachMobilePlan)))))
                         ;if the service provider is Starhub
                         (set! additionalDataUsageCost (* 6.42 (- dataUsage (cadddr(cdr eachMobilePlan)))))
                     )
                   )
              )
              ;Calculate the expected price user has to pay based on their input and the mobile plan
              (set! calculatedPrice (+ additionalOutgoingMinutesCost additionalSMSCost additionalDataUsageCost (car(cddddr(cdr eachMobilePlan)))))
              (set! calculatedPlans (append (list (modif-last-1 eachMobilePlan calculatedPrice)) calculatedPlans))
              
           )
       )
   )
 ;Append element into the list
 (define (modif-last-1 lst elem)
  (if (null? (cdr lst))
      (list elem)
      (cons (car lst) (modif-last-1 (cdr lst) elem))))
  
 ;Compare plans and find the minimum cost among plans
 (define (comparePlans plans)
   (cond
        ((null? plans) 
          (display ""))
        (else
             (if (zero? minPrice)
                 (set! minPrice (cadr(cddddr(car plans))))
             )
             (if (< (cadr(cddddr(car plans))) minPrice) 
                 (set! minPrice (cadr(cddddr(car plans))))
             )
             (comparePlans (cdr plans))
        )
    )
  )
 ;Traverse through the list and append to the best_plan list all plans with similar price
 (define (search-for-plans mlist)
   (cond
        ((null? mList) 
          (display ""))
        (else
           (if(= (cadr(cddddr(car mlist))) minPrice)
              (begin
                (set! best_plan (append (list (car mlist)) best_plan))
               )
           )
          (if(null? (cdr mList))
            (display "")
            (search-for-plans (cdr mList))
          )
        )
    )
 )
 
;Remove first best plan from the calculatedPlans
(define (remove-first-best rlist)
  (if (null? rlist)
      (display "")
      (begin 
         (if (= (cadr(cddddr(car rlist))) (cadr(cddddr(car best_plan))))
             (remove-first-best (cdr rlist))
             (begin
                (set! withoutFirstBest (append (list (car rlist)) withoutFirstBest))
                (remove-first-best (cdr rlist))
             )
         )
      )
  )
)

;Find the length of a list
(define (findList list)
     (if (null? list)
         (display "")
         (begin 
             (set! count (+ count 1))
             (if (null? (cdr list))
                 (display "")
                 (findList (cdr list))
             )
         )
     )  
)

;Function to reset variables to default value
(define (reset)
  (set! best_plan '())                           
  (set! calculatedPlans '())
  (set! minPrice 0)
  (set! withoutFirstBest '())
  (set! count 0)
)
;==================================================================================================
;Main
;===================================================================================================
(mainMenu) 
(display "Enter your choice")(newline)
(set! choice (read))
(selection)
 